package com.example.ormsqlsplicingframe1.orm.wrapper;


import com.baomidou.mybatisplus.annotation.TableField;
import com.example.ormsqlsplicingframe1.orm.Util.SelectWrapperPage;
import com.example.ormsqlsplicingframe1.orm.jdbc.SelectWrapperJdbc;
import com.example.ormsqlsplicingframe1.orm.note.Title;
import com.example.ormsqlsplicingframe1.orm.note.Date;
import com.example.ormsqlsplicingframe1.orm.note.Where;

import com.example.ormsqlsplicingframe1.orm.note.*;
import com.example.ormsqlsplicingframe1.processor.TranslatePostProcessor;
import com.example.ormsqlsplicingframe1.session.SqlPostProcessor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.maven.surefire.shade.org.apache.maven.shared.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class SelectWrapper<E> {
    private List<SqlPostProcessor> sqlPostProcessors =Arrays.asList(new TranslatePostProcessor());

    private String orderBy = "";

    private String count = "";

    private String sum = "";

    private String  groupBy = "";

    private String limit = "";

    private Integer pageSize = 10;

    private Integer pageIndex = 0;

    private Class<E> aClass;

    private Object object;

    private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");


    public SelectWrapper() {
    }

    public SelectWrapper(Object obj) {
        this.aClass = (Class<E>) obj.getClass();
        this.object = obj;
        try {
            wrapper();
        }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("获取字段值失败");
        }
    }

    public SelectWrapper(Class<E> aClass, Object object) {
        this.aClass = aClass;
        this.object = object;
        try {
            wrapper();
        }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("获取字段值失败");
        }
    }

    public SelectWrapper(Class<E> aClass) {
        this.aClass = aClass;
        try {
            wrapper();
        }catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("获取字段值失败");
        }

    }




    public SelectWrapper<E> orderByASC(String sortName){
        this.orderBy = sortName+" ASC ";
        return this;
    }

    public SelectWrapper<E> orderByDESC(String sortName){
        this.orderBy = sortName+" DESC ";
        return this;
    }

    public SelectWrapper<E> countAs(String columnName, String fieldName){
        this.count = "COUNT("+columnName+") AS "+fieldName+" ";
        return this;
    }

    public SelectWrapper<E> sumAs(String columnName, String fieldName){
        this.sum = "SUM("+columnName+") AS "+fieldName+" ";
        return this;
    }

    public SelectWrapper<E> groupBy(String columnName){
        this.groupBy = "GROUP BY "+columnName;
        return this;
    }


    public SelectWrapper<E> limit(){
        getPage(object);
        if (this.pageIndex == 0){
            this.limit = " LIMIT "+this.pageSize;
        }else {
            this.limit = " LIMIT "+this.pageIndex+" , "+this.pageSize;
        }

        return this;
    }

    private SelectWrapper<E> limit(int pageSize,int pageIndex){
        getPage(object, pageIndex, pageSize);
        if (this.pageIndex == 0){
            this.limit = " LIMIT "+this.pageSize;
        }else {
            this.limit = " LIMIT "+this.pageIndex+" , "+this.pageSize;
        }

        return this;
    }

    public SelectWrapper<E> page(Integer pageIndex, Integer pageSize){
        this.pageSize = pageSize;

        this.pageIndex = pageIndex;
        return this;
    }

    //获取 分页的数据   第几页：pageIndex 获取多少条数据：pageSize
    @SneakyThrows
    private void getPage(Object obj){

        if (obj !=null){

            Class<?> aClass = obj.getClass();

            Field[] fields = aClass.getDeclaredFields();

            boolean isExist = false;

            if (fields.length >0){
                for (Field field : fields) {
                    if ("pageSize".equals(field.getName())){
                        isExist =true;
                    }
                    if ("pageIndex".equals(field.getName())){
                        isExist =true;
                    }
                }
            }
            if (isExist){
                Field pageSize = aClass.getDeclaredField("pageSize");
                Field pageIndex = aClass.getDeclaredField("pageIndex");
                pageSize.setAccessible(true);
                pageIndex.setAccessible(true);

                Object o = pageSize.get(obj);
                Object o1 = pageIndex.get(obj);
                if (o != null && o1 != null){
                    this.pageIndex =(Integer) o1; ;
                    this.pageSize =(Integer) o;
                }else {
                    //给默认值
                    this.pageIndex  = 0;
                    this.pageSize =10;
                }
            }else {

                Object superObj = aClass.getSuperclass().newInstance();
                if (superObj.getClass() != Object.class){
                    getPage(superObj);
                }
            }
        }

    }

    @SneakyThrows
    private void getPage(Object obj,int index,int size){

        if (obj !=null){

            Class<?> aClass = obj.getClass();

            Field[] fields = aClass.getDeclaredFields();

            boolean isExist = false;

            if (fields.length >0){
                for (Field field : fields) {
                    if ("pageSize".equals(field.getName())){
                        isExist =true;
                    }
                    if ("pageIndex".equals(field.getName())){
                        isExist =true;
                    }
                }
            }
            if (isExist){
                Field pageSize = aClass.getDeclaredField("pageSize");
                Field pageIndex = aClass.getDeclaredField("pageIndex");
                pageSize.setAccessible(true);
                pageIndex.setAccessible(true);

                Object o = pageSize.get(obj);
                Object o1 = pageIndex.get(obj);
                if (o != null && o1 != null){

                    if ((Integer) o >0){
                        this.pageSize =(Integer) o;
                    }else {
                        this.pageSize =size;
                    }
                    this.pageIndex =(Integer) o1; ;

                }else {

                    //值从注解上面获取，如果没有，就使用默认值
                    if (index > 0 && size >= 0){
                        this.pageIndex  = index;
                        this.pageSize = size;
                    }else {
                        this.pageIndex  = 0;
                        this.pageSize =10;
                    }


                }
            }else {

                Object superObj = aClass.getSuperclass().newInstance();
                if (superObj.getClass() != Object.class){
                    getPage(superObj);
                }
            }
        }

    }

    //列名
    private Column column = new Column();

    //表名
    private com.example.ormsqlsplicingframe1.orm.wrapper.Title title = new com.example.ormsqlsplicingframe1.orm.wrapper.Title();

    //条件
    private com.example.ormsqlsplicingframe1.orm.wrapper.Where where = new com.example.ormsqlsplicingframe1.orm.wrapper.Where();

    //左连接
    private LeftJoin leftJoin = new LeftJoin();

    //右连接
    private RightJoin rightJoin = new RightJoin();

    //内链接
    private InnerJoin innerJoin = new InnerJoin();

    public com.example.ormsqlsplicingframe1.orm.wrapper.Title title(){
        return title;
    }

    public Column column(){
        return column;
    }

    public com.example.ormsqlsplicingframe1.orm.wrapper.Where where(){
        return where;
    }

    public LeftJoin leftJoin(){
        return leftJoin;
    }

    public RightJoin rightJoin(){
        return rightJoin;
    }

    public InnerJoin innerJoin(){
        return innerJoin;
    }

    //获取查询条件值
    public Map getParams(){
        return where.getParams();
    }

    public String getSql(){

        StringBuilder builder = new StringBuilder();

        builder.append(" select ");
        if (this.count!= null &&!this.count.equals("")){
            builder.append(this.count);
        }
        if (this.sum!= null &&!this.sum.equals("")){
            builder.append(this.sum);
        }
        builder.append(column.getSql()+" from "+title.getSql());
        String leftSql = leftJoin.getSql();
        String rightSql = rightJoin.getSql();
        String innerSql = innerJoin.getSql();
        if (leftSql!= null && !leftSql.equals("")){
            builder.append(leftSql);
        }
        if (rightSql!= null && !rightSql.equals("")){
            builder.append(rightSql);
        }

        if (innerSql!= null && !innerSql.equals("")){
            builder.append(innerSql);
        }


        String whereSql = where.getSql();
        if (whereSql != null && !whereSql.equals("")){
            builder.append(" where "+whereSql);
        }

        String groupBy = this.groupBy;

        if (groupBy != null && !groupBy.equals("")){
            builder.append(groupBy);
        }

        String orderBy = this.orderBy;
        if (orderBy != null && !orderBy.equals("")){
            builder.append(" order by "+ orderBy);
        }


        if (!this.limit.equals("")){
            builder.append(this.limit);
        }

        String string = builder.toString();

        for (SqlPostProcessor sqlPostProcessor : sqlPostProcessors) {
            string= sqlPostProcessor.enhance(string,aClass);
        }

        log.info(string);
        return string;
    }

    private void wrapper() throws IllegalAccessException {
        Title annotation = aClass.getAnnotation(Title.class);
        title().title(annotation.titleName());

        String on = annotation.on();

        String leftJoin = annotation.leftJoin();
        if (leftJoin != null && !leftJoin.equals("")&&on!= null &&!on.equals("")){
            leftJoin().leftTitle(leftJoin,on);
        }

        String innerJoin = annotation.innerJoin();
        if (innerJoin != null && !innerJoin.equals("")&&on!= null &&!on.equals("")){
            innerJoin().innerTitle(innerJoin,on);
        }

        String rightJoin = annotation.rightJoin();
        if (rightJoin != null && !rightJoin.equals("")&&on!= null &&!on.equals("")){
            rightJoin().rightTitle(rightJoin,on);
        }

        String[] rightJoinList = annotation.rightJoinList();

        if (rightJoinList != null && rightJoinList.length>0){
            for (final String s : rightJoinList) {
                rightJoin().rightTitle(s);
            }
        }

        String[] leftJoinList = annotation.leftJoinList();

        if (leftJoinList != null && leftJoinList.length>0){
            for (final String s : leftJoinList) {
                leftJoin().leftTitle(s);
            }
        }

        String[] innerJoinList = annotation.innerJoinList();

        if (innerJoinList != null && innerJoinList.length>0){
            for (final String s : innerJoinList) {
                innerJoin().innerTitle(s);
            }
        }

        Limit limit = aClass.getAnnotation(Limit.class);
        if (limit != null){
            if (limit.isNotPage()){
                this.limit(limit.pageSize(),limit.pageIndex());
            }
        }

        WhetherExecuteNew whetherExecuteNew = aClass.getAnnotation(WhetherExecuteNew.class);
        if (whetherExecuteNew != null){
            if (whetherExecuteNew.isExecuteNew()){
                where().whetherExecuteNew();
            }
        }

        Field[] fields = aClass.getDeclaredFields();



        boolean is = false;

        Map<String, Field> map = null;
        if (object != null){
            Field[] declaredFields = object.getClass().getDeclaredFields();
             map = Arrays.stream(declaredFields).collect(Collectors.toMap(field -> field.getName(), field -> field));
             is = true;
        }

        int a =0;
        for (final Field field : fields) {

            if (field.getAnnotation(TableField.class) != null){
                if (field.getAnnotation(TableField.class).exist()){
                    oldAndNew(map,is,field,a);
                }
            }
        }
    }

    //是否执行旧写法还是新写法
    private void oldAndNew( Map<String, Field> map,boolean isTrue,Field field,int a) throws IllegalAccessException {
        field.setAccessible(true);
        //字段上的注解
        String columnName = field.getAnnotation(TableField.class).value();
        //字段名
        String fieldName = field.getName();

        if (isTrue){
            Where where = field.getAnnotation(Where.class);
            if (where != null){
                selectWhere(map,fieldName,columnName,where);
            }

            Date date = field.getAnnotation(Date.class);
            if (date != null){
                selectDate(date,map,columnName);
            }

            Limit limit = field.getAnnotation(Limit.class);
            if (limit != null){
                if (limit.isNotPage()){
                    this.limit();
                }
            }


            parentMethod(field,a++);
            column().add(columnName,fieldName);
        }else {
            parentMethod(field,a++);
            column().add(columnName,fieldName);
        }
    }

    /**
     *
     * @param map po类
     * @param fieldName  vo字段名
     * @param columnName vo 注解别名
     * @param where  注解
     * @throws IllegalAccessException
     */
    private void selectWhere(Map<String, Field> map, String fieldName,String columnName, Where where) throws IllegalAccessException {

        if (StringUtils.isNotEmpty(where.customization())){
            columnName = where.customization();
        }
        if (StringUtils.isNotEmpty(where.substitution())){
            fieldName = where.substitution();
        }

        Field objField = map.get(fieldName);
        if (objField != null){
            objField.setAccessible(true);
            Object value = objField.get(object);
            if (value != null && !value.equals("")){

                if (StringUtils.isNotBlank(where.eq())){
                    where().eq(columnName,value);
                }
                if (StringUtils.isNotBlank(where.noEq())){
                    where().not(columnName,value);
                }
                if (StringUtils.isNotBlank(where.overallLike())){
                    where().overallLike(columnName,value);
                }
                if (StringUtils.isNotBlank(where.leftLike())){
                    where().leftLike(columnName,value);
                }
                if (StringUtils.isNotBlank(where.rightLike())){
                    where().rightLike(columnName,value);
                }
            }
        }

    }

    private void selectDate(Date selectDate, Map<String, Field> map, String columnName) throws IllegalAccessException {
        if (!selectDate.value().equals("")){
            //如果查询有逗号，就分割开来
            int i = selectDate.value().indexOf(",");
            if (i >0){
                Field objField = map.get(selectDate.value().substring(0, i));
                Field objField2 = map.get(selectDate.value().substring(i+1));
                if (objField != null && objField2 != null){
                    objField.setAccessible(true);
                    objField2.setAccessible(true);
                    java.util.Date date =(java.util.Date) objField.get(object);
                    java.util.Date date2 =(java.util.Date) objField2.get(object);
                    if (date != null && date2 != null){
                        where().dateFORMAT(columnName,date,date2);
                    }else {
                        if (date != null){
                            where().dateFORMAT(columnName,date);
                        }
                        if (date2 != null){
                            where().dateFORMAT(columnName,date2);
                        }
                    }
                }
            }else {
                Field objField = map.get( selectDate.value());
                if (objField != null){
                    objField.setAccessible(true);
                    java.util.Date date =(java.util.Date) objField.get(object);
                    if (date != null){
                        where().dateFORMAT(columnName,date);
                    }
                }
            }
        }
    }

    private void parentMethod(Field field,int a){
        CountExists(field,a);
        SumExists(field,a);
        OrderByExists(field);
        groupBy(field);
    }

    /**
     *
     * @param field java字段
     * @param a 记录前面是否存在字段
     */
    private void CountExists(Field field,int a ){
        String b = "";
        String fieldName = field.getName();
        if (field.getAnnotation(CountAs.class)!= null && !field.getAnnotation(CountAs.class).equals("")){
            if (a>0){
                b = " , ";
            }
            CountAs countAs = field.getAnnotation(CountAs.class);
            if (countAs.value()!=null && !countAs.value().equals("")){
                if (countAs.anotherName()!= null && !countAs.anotherName().equals("")){
                    countAs(countAs.value(),countAs.anotherName()+b);
                }else {
                    countAs(countAs.value(),fieldName+b);
                }

            }
        }
    }

    private void SumExists(Field field,int a){
        String b = "";
        String fieldName = field.getName();
        if (field.getAnnotation(SumAs.class)!= null && !field.getAnnotation(SumAs.class).equals("")){
            if (a>0){
                b = " , ";
            }
            SumAs countAs = field.getAnnotation(SumAs.class);
            if (countAs.value()!=null && !countAs.value().equals("")){
                if (countAs.anotherName()!= null && !countAs.anotherName().equals("")){
                    sumAs(countAs.value(),countAs.anotherName()+b);
                }else {
                    sumAs(countAs.value(),fieldName+b);
                }

            }
        }
    }

    private void OrderByExists(Field field){
        if (field.getAnnotation(OrderBy.class)!= null && !field.getAnnotation(OrderBy.class).equals("")){
            OrderBy orderBy = field.getAnnotation(OrderBy.class);
            if (orderBy.isASC()){
                orderByASC(orderBy.value());
            }else {
                orderByDESC(orderBy.value());
            }
        }
    }

    private void groupBy(Field field){
        if (field.getAnnotation(GroupBy.class)!=null){
            String value = field.getAnnotation(GroupBy.class).value();
            if (value != null && !value.equals("")){
                groupBy(value);
            }
        }
    }



    private static final Logger logger = LoggerFactory.getLogger(SelectWrapper.class);
    public List<E> selectList(){
        log();
        return SelectWrapperJdbc.select(getSql(),where().getValues(),aClass);
    }



    public E selectOnt(){
        log();
        return SelectWrapperJdbc.select(getSql(),where().getValues(),aClass).get(0);
    }

    //分页目前是wrapper哪里可以对分页赋值，也可以通过对象或者继承的对象来赋值 下次更新可以在通过注解执行分页，或者返回结果类哪里要求
    public SelectWrapperPage<E> selectPage(){

        log();
        SelectWrapperPage<E> page = new SelectWrapperPage();
        page.setTotal(sum());
        List<E> select =   SelectWrapperJdbc.select(getSql(),where().getValues(),aClass);
        page.setList(select);

        return page;
    }

    private void log(){

        logger.info("这是sql日志"+getSql());

        List<Object> values = where().getValues();
        ArrayList<Object> objects = new ArrayList<>(values.size());
        values.forEach(row ->{
            objects.add(disposeDate(row)+"("+row.getClass().getSimpleName()+")");
        });
        logger.info(values.toString());

    }

    //处理时间
    private Object disposeDate(Object obj){

        if (obj.getClass()== java.util.Date.class){
            return dateFormat.format(obj);
        }
        return obj;
    }

    public int sum(){
        String sql = getSql();
        int index = sql.indexOf("LIMIT");
        String substring = sql.substring(0,index);
        List<E> select =  SelectWrapperJdbc.select(substring, where().getValues(), aClass);
        return select.size();
    }

     public  static <E> SelectWrapper<E> newInstance(Class<E> aClass) {

        SelectWrapper<E> fragment = new SelectWrapper(aClass);
        return fragment;
    }


    public static <E> SelectWrapper<E> newInstance(Class<E> aClass, Object aObject) {

        SelectWrapper<E> fragment = new SelectWrapper(aClass,aObject);
        return fragment;
    }


}
